package org.example.project.common.config;

import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.example.project.common.contant.Log;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.util.StreamUtils;
import org.springframework.web.client.ResponseErrorHandler;
import org.springframework.web.client.RestTemplate;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.nio.charset.Charset;
import java.time.Duration;
import java.time.LocalDateTime;

/**
 * @author wenxy
 * @date 2020/11/9
 */
@Configuration
@EnableConfigurationProperties(RestTemplateConfiguration.RestTemplateProperties.class)
@Slf4j
public class RestTemplateConfiguration {

    @Bean
    RestTemplate restTemplate(RestTemplateBuilder restTemplateBuilder, RestTemplateProperties restTemplateProperties) {
        restTemplateBuilder.setReadTimeout(Duration.ofMillis(restTemplateProperties.getReadTimeout()));
        restTemplateBuilder.setConnectTimeout(Duration.ofMillis(restTemplateProperties.getConnectTimeout()));
        restTemplateBuilder.errorHandler(responseErrorHandler());
        return restTemplateBuilder.build();
    }

    @ConfigurationProperties(prefix = "remote")
    @Data
    public static class RestTemplateProperties {
        int connectTimeout = 2000;
        int readTimeout = 30000;
    }

    /**
     * responseErrorHandler
     * 默认情况下，restTemplate对于非200的响应，都直接抛出异常。
     * 一般我们可能不希望拿到异常而是获取响应判断响应状态等
     * 这里拦截错误处理，仅记录日志
     *
     * @return ResponseErrorHandler
     */
    ResponseErrorHandler responseErrorHandler() {
        return new ResponseErrorHandler() {
            @Override
            public boolean hasError(ClientHttpResponse response) throws IOException {
                int rawStatusCode = response.getRawStatusCode();
                HttpStatus.Series series = HttpStatus.Series.resolve(rawStatusCode);
                return (series == HttpStatus.Series.CLIENT_ERROR || series == HttpStatus.Series.SERVER_ERROR);
            }

            @Override
            public void handleError(ClientHttpResponse response) {
                String body = getResponseBody(response);
                // 这里response明细类型没暴露，拿不到请求相关信息，仅记录了响应结果
                // 如果业务判断响应需要抛出异常，可被restLogAspect拦截，并记录restTemplate调用参数和结果
                log.warn(Log.builder()
                        .result(body)
                        .createTime(LocalDateTime.now())
                        .build().toString());
            }

            protected String getResponseBody(ClientHttpResponse response) {
                try {
                    ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(StreamUtils.BUFFER_SIZE);
                    StreamUtils.copy(response.getBody(), byteArrayOutputStream);
                    return new String(byteArrayOutputStream.toByteArray(), getCharset(response));
                } catch (IOException ex) {
                    // ignore
                }
                return "";
            }

            protected Charset getCharset(ClientHttpResponse response) {
                HttpHeaders headers = response.getHeaders();
                MediaType contentType = headers.getContentType();
                return (contentType != null ? contentType.getCharset() : Charset.defaultCharset());
            }
        };
    }

}
